#ifndef AER_OPENGL_HPP_
#define AER_OPENGL_HPP_

#include "aerDefs.hpp"
#include <GL/glew.h>


#ifdef NDEBUG
# define CHECKGLERROR()   
#else
# define CHECKGLERROR()   aer::gl::CheckError( __FILE__, __LINE__, "", true)
#endif


namespace aer {
namespace gl {

bool Initialize();
const int Geti( GLenum pname );

// dirty error checking functions
const char* GetErrorString(GLenum err);
void CheckError(const char *file, const int line, const char *errMsg="", bool bExitOnFail=false);

class Info
{
 public:
  static const unsigned char* GetVendor() {
    return glGetString(GL_VENDOR);
  }
  
  static const unsigned char* GetRenderer() {
    return glGetString(GL_RENDERER);
  }
  
  static const unsigned char* GetVersion() {
    return glGetString(GL_VERSION);
  }
  
  static const unsigned char* GetGLSLVersion() {
    return glGetString(GL_SHADING_LANGUAGE_VERSION);
  }
  
  static const int GetMinorVersion() 
  {
    GLint param;
    glGetIntegerv( GL_MINOR_VERSION, &param);
    return param;
  }
  
  static const int GetMajorVersion() 
  {
    GLint param;
    glGetIntegerv( GL_MAJOR_VERSION, &param);
    return param;
  }

// more info: http://developer.download.nvidia.com/opengl/specs/GL_NVX_gpu_memory_info.txt
# define GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX    0x9048
# define GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX  0x9049

  static const int GetAvailableMemory()
  {
    GLint param;
    glGetIntegerv( GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &param);
    return param;
  }
  
  static const int GetCurrentMemory()
  {
    GLint param;
    glGetIntegerv( GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &param);
    return param;
  }

# undef GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX
# undef GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX

};

class Constant
{ 
 public:  
  static const int MaxClipDistances()     { return Geti( GL_MAX_CLIP_DISTANCES ); }
  static const int MaxCubeMapTextureSize(){ return Geti( GL_MAX_CUBE_MAP_TEXTURE_SIZE ); }
  static const int MaxColorAttachments()  { return Geti( GL_MAX_COLOR_ATTACHMENTS ); }
  static const int MaxDrawBuffer()        { return Geti( GL_MAX_DRAW_BUFFERS ); }
  static const int MaxTextureImageUnits() { return Geti( GL_MAX_TEXTURE_IMAGE_UNITS ); }
  static const int MaxTextureSize()       { return Geti( GL_MAX_TEXTURE_SIZE ); }
  static const int MaxTextureAnisotropy() { return Geti( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT ); }
  static const int MaxVertexAttribs()     { return Geti( GL_MAX_VERTEX_ATTRIBS ); }
  
};

class State
{
  public:
    enum Capability
    {
      DEPTH_TEST    = GL_DEPTH_TEST,
      STENCIL_TEST  = GL_STENCIL_TEST,
      BLEND         = GL_BLEND,
      CULL_FACE     = GL_CULL_FACE,
      MULTISAMPLE   = GL_MULTISAMPLE
    };
    
    struct Status
    {
      bool bDepthTest;
      bool bStencilTest;
      bool bBlend;      
      bool bCullFace;
      bool bMultisample;
    };
      
  
  public:
    static Status DumpStates() 
    { 
      AER_ASSERT( "todo" && 0 ); 
      Status status;
      
      status.bDepthTest   = IsEnabled( DEPTH_TEST );
      status.bStencilTest = IsEnabled( STENCIL_TEST );
      status.bBlend       = IsEnabled( BLEND );
      status.bCullFace    = IsEnabled( CULL_FACE );
      status.bMultisample = IsEnabled( MULTISAMPLE );
      // TODO
      
      return status;
    }
    
    static void SetViewport( int x, int y, int w, int h) { glViewport(x,y,w,h); }// ~
    
    static void SetStates(const Status &status) 
    { 
      AER_ASSERT( "todo" && 0 ); 
    }
    
    
    static void Enable(Capability cap)     { glEnable(cap); }
    static void Disable(Capability cap)    { glDisable(cap); }
    static bool IsEnabled(Capability cap)  { return glIsEnabled(cap) == GL_TRUE; }
    
        
    static void ColorMask( bool red, bool green, bool blue, bool alpha) {
      glColorMask( red, green, blue, alpha);
    }
    
    static void DepthMask( bool depth ) {
      glDepthMask( depth );
    }
    
    
    static void ClearColor( float r, float g, float b, float a) {
      glClearColor( r, g, b, a);
    }
    
    
    static void EnableVertexAttribArray(unsigned int id) {
      glEnableVertexAttribArray( id );
    }
    
    static void DisableVertexAttribArray(unsigned int id) {
      glDisableVertexAttribArray( id );
    }
    
    static bool IsVertexAttribArrayEnabled(unsigned int id) {
      GLint param;
      glGetVertexAttribiv( id, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &param);
      return (param != 0);
    }
    
};

class Error
{
};

//aer::gl::Error::Check();

} // gl
} // aer

#endif // AER_OPENGL_HPP_

